3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
22 * Generic operation result class
23 * Has warning/error list, boolean status and arbitrary value
25 * "Good" means the operation was completed with no warnings or errors.
27 * "OK" means the operation was partially or wholly completed.
29 * An operation which is not OK should have errors so that the user can be
30 * informed as to what went wrong. Calling the fatal() function sets an error
31 * message and simultaneously switches off the OK flag.
33 * The recommended pattern for Status objects is to return a StatusValue
34 * unconditionally, i.e. both on success and on failure -- so that the
35 * developer of the calling code is reminded that the function can fail, and
36 * so that a lack of error-handling will be explicit.
38 * The use of Message objects should be avoided when serializability is needed.
48 protected $errors = [];
53 /** @var bool[] Map of (key => bool) to indicate success of each part of batch operations */
56 /** @var int Counter for batch operations */
57 public $successCount = 0;
59 /** @var int Counter for batch operations */
60 public $failCount = 0;
63 * Factory function for fatal errors
65 * @param string|MessageSpecifier $message Message key or object
66 * @param mixed ...$parameters
69 public static function newFatal( $message, ...$parameters ) {
70 $result = new static();
71 $result->fatal( $message, ...$parameters );
76 * Factory function for good results
78 * @param mixed|null $value
81 public static function newGood( $value = null ) {
82 $result = new static();
83 $result->value
= $value;
88 * Splits this StatusValue object into two new StatusValue objects, one which contains only
89 * the error messages, and one that contains the warnings, only. The returned array is
92 * 0 => object(StatusValue) # the StatusValue with error messages, only
93 * 1 => object(StatusValue) # The StatusValue with warning messages, only
98 public function splitByErrorType() {
99 $errorsOnlyStatusValue = clone $this;
100 $warningsOnlyStatusValue = clone $this;
101 $warningsOnlyStatusValue->ok
= true;
103 $errorsOnlyStatusValue->errors
= $warningsOnlyStatusValue->errors
= [];
104 foreach ( $this->errors
as $item ) {
105 if ( $item['type'] === 'warning' ) {
106 $warningsOnlyStatusValue->errors
[] = $item;
108 $errorsOnlyStatusValue->errors
[] = $item;
112 return [ $errorsOnlyStatusValue, $warningsOnlyStatusValue ];
116 * Returns whether the operation completed and didn't have any error or
121 public function isGood() {
122 return $this->ok
&& !$this->errors
;
126 * Returns whether the operation completed
130 public function isOK() {
137 public function getValue() {
142 * Get the list of errors
144 * Each error is a (message:string or MessageSpecifier,params:array) map
148 public function getErrors() {
149 return $this->errors
;
153 * Change operation status
157 public function setOK( $ok ) {
162 * Change operation result
164 * @param bool $ok Whether the operation completed
165 * @param mixed|null $value
167 public function setResult( $ok, $value = null ) {
168 $this->ok
= (bool)$ok;
169 $this->value
= $value;
175 * @param string|MessageSpecifier $message Message key or object
176 * @param mixed ...$parameters
178 public function warning( $message, ...$parameters ) {
181 'message' => $message,
182 'params' => $parameters
187 * Add an error, do not set fatal flag
188 * This can be used for non-fatal errors
190 * @param string|MessageSpecifier $message Message key or object
191 * @param mixed ...$parameters
193 public function error( $message, ...$parameters ) {
196 'message' => $message,
197 'params' => $parameters
202 * Add an error and set OK to false, indicating that the operation
203 * as a whole was fatal
205 * @param string|MessageSpecifier $message Message key or object
206 * @param mixed ...$parameters
208 public function fatal( $message, ...$parameters ) {
211 'message' => $message,
212 'params' => $parameters
218 * Merge another status object into this one
220 * @param StatusValue $other
221 * @param bool $overwriteValue Whether to override the "value" member
223 public function merge( $other, $overwriteValue = false ) {
224 $this->errors
= array_merge( $this->errors
, $other->errors
);
225 $this->ok
= $this->ok
&& $other->ok
;
226 if ( $overwriteValue ) {
227 $this->value
= $other->value
;
229 $this->successCount +
= $other->successCount
;
230 $this->failCount +
= $other->failCount
;
234 * Returns a list of status messages of the given type
236 * Each entry is a map of:
237 * - message: string message key or MessageSpecifier
238 * - params: array list of parameters
240 * @param string $type
243 public function getErrorsByType( $type ) {
245 foreach ( $this->errors
as $error ) {
246 if ( $error['type'] === $type ) {
255 * Returns true if the specified message is present as a warning or error
257 * @param string|MessageSpecifier $message Message key or object to search for
261 public function hasMessage( $message ) {
262 if ( $message instanceof MessageSpecifier
) {
263 $message = $message->getKey();
265 foreach ( $this->errors
as $error ) {
266 if ( $error['message'] instanceof MessageSpecifier
267 && $error['message']->getKey() === $message
270 } elseif ( $error['message'] === $message ) {
279 * If the specified source message exists, replace it with the specified
280 * destination message, but keep the same parameters as in the original error.
282 * Note, due to the lack of tools for comparing IStatusMessage objects, this
283 * function will not work when using such an object as the search parameter.
285 * @param MessageSpecifier|string $source Message key or object to search for
286 * @param MessageSpecifier|string $dest Replacement message key or object
287 * @return bool Return true if the replacement was done, false otherwise.
289 public function replaceMessage( $source, $dest ) {
292 foreach ( $this->errors
as $index => $error ) {
293 if ( $error['message'] === $source ) {
294 $this->errors
[$index]['message'] = $dest;
305 public function __toString() {
306 $status = $this->isOK() ?
"OK" : "Error";
307 if ( count( $this->errors
) ) {
308 $errorcount = "collected " . ( count( $this->errors
) ) . " error(s) on the way";
310 $errorcount = "no errors detected";
312 if ( isset( $this->value
) ) {
313 $valstr = gettype( $this->value
) . " value set";
314 if ( is_object( $this->value
) ) {
315 $valstr .= "\"" . get_class( $this->value
) . "\" instance";
318 $valstr = "no value set";
320 $out = sprintf( "<%s, %s, %s>",
325 if ( count( $this->errors
) > 0 ) {
326 $hdr = sprintf( "+-%'-4s-+-%'-25s-+-%'-40s-+\n", "", "", "" );
330 foreach ( $this->errors
as $error ) {
331 if ( $error['message'] instanceof MessageSpecifier
) {
332 $key = $error['message']->getKey();
333 $params = $error['message']->getParams();
334 } elseif ( $error['params'] ) {
335 $key = $error['message'];
336 $params = $error['params'];
338 $key = $error['message'];
342 $out .= sprintf( "| %4d | %-25.25s | %-40.40s |\n",
345 implode( " ", $params )